//
// Created by DELL on 2021/3/20.
//
#include "http/servlet.h"

#include <memory>
#include <fnmatch.h>
namespace sylar {
    namespace http {
        FunctionServlet::FunctionServlet(callback cb)
                : Servlet("FunctionServlet"), m_cb(cb) {
        }

        int32_t FunctionServlet::handle(HttpRequest::ptr request,
                                        HttpResponse::ptr response,
                                        HttpSession::ptr session) {
            return m_cb(request, response, session);
        }

        ServletDispatch::ServletDispatch()
                : Servlet("ServletDispatch") {
            m_default.reset(new NotFoundServlet("sylar/1.0.0"));
        }

        int32_t ServletDispatch::handle(HttpRequest::ptr request,
                                        HttpResponse::ptr response,
                                        HttpSession::ptr session) {
            auto servlet = getMatchedServlet(request->getPath());
            if (servlet) {
                servlet->handle(request, response, session);
            }
            return 0;
        }

        void ServletDispatch::addServlet(const std::string &uri, Servlet::ptr servlet) {
            RWMutexType::WriteLock lock(m_mutex);
            m_data[uri] = servlet;
        }

        void ServletDispatch::addServlet(const std::string &uri, FunctionServlet::callback cb) {
            RWMutexType::WriteLock lock(m_mutex);
            m_data[uri].reset(new FunctionServlet(cb));
        }

        void ServletDispatch::addGlobServlet(const std::string &uri, Servlet::ptr servlet) {
            RWMutexType::WriteLock lock(m_mutex);
            for (auto it = m_globs.begin(); it != m_globs.end(); it++) {
                if (it->first == uri) {
                    m_globs.erase(it);
                    break;
                }
            }
            m_globs.emplace_back(uri, servlet);
        }

        void ServletDispatch::addGlobServlet(const std::string &uri, FunctionServlet::callback cb) {
            addGlobServlet(uri, std::make_shared<FunctionServlet>(cb));
        }

        void ServletDispatch::delServlet(const std::string &uri) {
            RWMutexType::WriteLock lock(m_mutex);
            m_data.erase(uri);
        }

        void ServletDispatch::delGlobServlet(const std::string &uri) {
            RWMutexType::WriteLock lock(m_mutex);
            for (auto it = m_globs.begin(); it != m_globs.end(); it++) {
                if (it->first == uri) {
                    m_globs.erase(it);
                    break;
                }
            }
        }

        Servlet::ptr ServletDispatch::getServlet(const std::string &uri) {
            RWMutexType::ReadLock lock(m_mutex);
            auto it = m_data.find(uri);
            return it == m_data.end() ? nullptr : it->second;
        }

        Servlet::ptr ServletDispatch::getGlobServlet(const std::string &uri) {
            RWMutexType::ReadLock lock(m_mutex);
            for (auto &m_glob : m_globs) {
                if (fnmatch( m_glob.first.c_str(),uri.c_str(),0)) {
                    return m_glob.second;
                }
            }
            return nullptr;
        }

        Servlet::ptr ServletDispatch::getMatchedServlet(const std::string &uri) {
            RWMutexType::ReadLock lock(m_mutex);
            auto mit = m_data.find(uri);
            if (mit != m_data.end()) {
                return mit->second;
            }
            for (auto it = m_globs.begin(); it != m_globs.end();it++) {
                if (!fnmatch( it->first.c_str(),uri.c_str(),0)) {
                    return it->second;
                }
            }
            return m_default;
        }

        NotFoundServlet::NotFoundServlet(const std::string &name)
                : Servlet("NotFoundServlet"),
                  m_name(name) {
            m_content = "<html><head><title>404 Not Found"
                        "</title></head><body><center><h1>404 Not Found</h1></center>"
                        "<hr><center>" + name + "</center></body></html>";
        }

        int32_t NotFoundServlet::handle(HttpRequest::ptr request,
                                        HttpResponse::ptr response,
                                        HttpSession::ptr session) {
            response->setStatus(HttpStatus::NOT_FOUND);
            response->setHeader("Server", "sylar/1.0.0");
            response->setHeader("Content-Type", "text/html");
            response->setBody(m_content);
            return 0;
        }

    }
}
